package cn.mong.util.modelexcel;

import java.awt.Point;
import java.util.Collections;
import java.util.List;

import org.apache.poi.xssf.usermodel.XSSFCellStyle;

/**
 * 抽象excel写入器
 * 
 * @author mengxianming@2021-9-15 9:48:12
 *
 * @param <D>
 */
public abstract class AbstractModelWriter<D> implements ModelWriter<D>{
    private Class<D> modelClazz;
    
    private CellWriter cellWriter;

    private XSSFCellStyle headerCellStyle;
    
    private int _baseRow;
    
    private int _baseCol;

  

    @Override
    public CellWriter getCellWriter() {
        return cellWriter;
    }
    
    @Override
    public List<? extends PositionCell> resolveHeaderCells() {
        return Collections.emptyList();     
    }


    @Override
    public Point writeModels(Iterable<D> models, boolean writeHeader) {
        int maxX = 0;
        int maxY = 0;
        Point curPos = new Point(this.getBaseCol(), this.getBaseRow());
        if(writeHeader) {
            // 扩展层级
            List<? extends PositionCell> headerCells = this.resolveHeaderCells();

            Point size = this.writeHeaders0(this.getBaseRow(), this.getBaseCol(), headerCells);
            maxX = Math.max(size.x, maxX);
            maxY = Math.max(size.y, maxY);
            curPos.y += size.y;
        }

        // 写各行记录
        for(D vo : models) {
            Point size = writeModelToRows(curPos.y, curPos.x, vo);
            maxX = Math.max(size.x, maxX);
            maxY = Math.max(size.y, maxY);
            curPos.y += size.y;
        }

        return new Point(maxX, curPos.y - this.getBaseRow());
    }

    /*
     * 写入列标题，返回占用占用总行数,总列数
     */
    protected Point writeHeaders0(int baseRow, int baseCol, List<? extends PositionCell> headerCells) {
       
        headerCells.forEach(hc -> {
            int curRow = baseRow + hc.getRow();
            int curCol = baseCol + hc.getCol();
            writeCellValue(curRow, curCol, hc.getVal(), new Point(hc.getXspan(), hc.getYspan()), hc.getCellStyleCustomizer(), true);
            // 设置列宽
            getCellWriter().setColumnWidth(curCol, hc.getXwidth());
        });
        
        return getMaxColRow(headerCells);
    }

    protected Point getMaxColRow(List<? extends PositionCell> cells) {
        Integer maxRow = cells.stream()
                .map(cell -> cell.getRow() + cell.getYspan() - 1)
                .max((i1, i2) -> i1 - i2).orElse(0);
        Integer maxCol = cells.stream()
                .map(cell -> cell.getCol() + cell.getXspan() - 1)
                .max((i1, i2) -> i1 - i2).orElse(0);

        return new Point(maxCol + 1, maxRow + 1);
    }


    /**
     * 把单个模型数据写入excel。返回占用的总行数,总列数
     * @param ew
     * @param vo
     * @param curRow
     * @return
     */
    private Point writeModelToRows(int baseRow, int baseCol, D model) {
        List<? extends PositionCell> valCells = this.modelToValCells(model);
        for(PositionCell vc : valCells) {
            writeCellValue(baseRow, baseCol, vc);
        }

        return getMaxColRow(valCells);
    }


    /**
     * 写入单元格
     * @param baseRow
     * @param baseCol
     * @param vc
     */
    protected void writeCellValue(int baseRow, int baseCol, PositionCell vc) {
        writeCellValue(baseRow + vc.getRow(), baseCol + vc.getCol(),
                vc.getVal(), new Point(vc.getXspan(), vc.getYspan()), vc.getCellStyleCustomizer(), false);
    }

    /**
     * 
     * @param row 从0开始的行号
     * @param col 从0开始的列号
     * @param val 单元格的值
     * @param width 单元格的字符宽度
     * 
     * @param mergeOption 合并单元格的参数。x表示合并的列数，应大于等于1；y表示合并的行数,应大于等于1.
     * @param cellStyleCustomizer 
     */
    protected void writeCellValue(int row, int col, Object val, Point mergeOption, CellStyleCustomizer cellStyleCustomizer, boolean isHeader) {
        getCellWriter().writeCellValue(row, col, val, mergeOption, (cellStyle) -> {

            XSSFCellStyle result = isHeader? headerCellStyle : cellStyle;
            if(cellStyleCustomizer != null) {
                result = cellStyleCustomizer.doCustomize(result);
            }

            return result;
        }, false);
    }


    public void setCellWriter(CellWriter cellWriter) {
        this.cellWriter = cellWriter;
    }
    
    public XSSFCellStyle getHeaderCellStyle() {
        return headerCellStyle;
    }
    
    public void setHeaderCellStyle(XSSFCellStyle headerCellStyle) {
        this.headerCellStyle = headerCellStyle;
    }

    
    public int getBaseRow() {
        return this._baseRow;
    }

    public int getBaseCol() {
        return this._baseCol;
    }
    
    @Override
    public void setBaseCol(int baseCol) {
        this._baseCol = baseCol;
    }
    
    @Override
    public void setBaseRow(int baseRow) {
        this._baseRow = baseRow;
    }
    
    @Override
    public Class<?> getModelClass() {
        return modelClazz;
    }
    
    public void setModelClass(Class<D> modelClazz) {
        this.modelClazz = modelClazz;
    }

}